Load libraries

library(tcR, quietly = T) # library to process TCR repertoire samples
Warning messages:
1: In scan(file = file, what = what, sep = sep, quote = quote, dec = dec,  :
  EOF within quoted string
2: In scan(file = file, what = what, sep = sep, quote = quote, dec = dec,  :
  EOF within quoted string
3: In scan(file = file, what = what, sep = sep, quote = quote, dec = dec,  :
  EOF within quoted string
4: In scan(file = file, what = what, sep = sep, quote = quote, dec = dec,  :
  EOF within quoted string
5: In scan(file = file, what = what, sep = sep, quote = quote, dec = dec,  :
  EOF within quoted string
6: In scan(file = file, what = what, sep = sep, quote = quote, dec = dec,  :
  EOF within quoted string
7: In scan(file = file, what = what, sep = sep, quote = quote, dec = dec,  :
  EOF within quoted string
8: In scan(file = file, what = what, sep = sep, quote = quote, dec = dec,  :
  EOF within quoted string
library(ggseqlogo) # plot logos of sequence motifs
library(pheatmap) # plot (pretty) heatmaps
library(plotly) # interactive plotting

Data processing

Example commands for using MixCR to process repertoire sequencing data Parallelizing and multithreading these steps is highly advised. Single threaded analysis can run for ~2 hours More info in: https://github.com/milaboratory/mixcr

# align with TCR reference
## alignment can be done only against reference of interest (if testing TCR beta repertoire only align against those)
mixcr align -r sample_TCRA.log -t 10 -f -c TRA -s hs sample_TCRA_1.fastq sample_TCRA_1.fastq sample_TCRA.vdjca
mixcr align -r sample_TCRB.log -t 10 -f -c TRB -s hs sample_TCRB_1.fastq sample_TCRB_1.fastq sample_TCRB.vdjca

# TCR sequence assembly
mixcr assemble -r sample_TCRA.log -t 10 -f sample_TCRA.vdjca sample_TCRA.clns
mixcr assemble -r sample_TCRB.log -t 10 -f sample_TCRA.vdjca sample_TCRB.clns

# export output to a readable table format
mixcr exportClones sample_TCRA.clns sample_TCRA.txt
mixcr exportClones sample_TCRB.clns sample_TCRB.txt

Data preparation

Load data (approx. 5min with this example)

system("gzip -d test_data/*gz") # unzip test files
cln_txt = list.files(path = "./test_data/", pattern = ".txt") # list all test files
cln_glio_raw = lapply(paste0("./test_data/", cln_txt), 
                       function(x){parse.mixcr(x)}) # parse test files
names(cln_glio_raw) = sapply(strsplit(cln_txt, ".", fixed = T), function(x) x[1])

# get sample metadata from file names
metadata = data.frame(t(sapply(strsplit(names(cln_glio_raw), "_"), rbind)), stringsAsFactors = F)
rownames(metadata) = names(cln_glio_raw)
colnames(metadata) = c("condition", "repertoire", "tissue")

#save(metadata, cln_glio_raw, file = "all_test_data.RData")

Load .RData with all needed objects (faster than previous code)

load("all_test_data.RData")

Out of frame TCR sequences are non functional but can still appear. In https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3872299/: “…out-of-frame (i.e., non-functional and thus not subjected to selection) and in-frame TCR beta repertoires, we also show the extent of the impact of thymic selection and the common trends in how this process shapes individual repertoires.”" Filtering clonotypes in frame/not in frame:

cln_glio_list <- get.inframes(cln_glio_raw)

Basic stats about the dataset

Compute basic stats

basic_stats = cloneset.stats(cln_glio_list)
basic_stats
                   #Nucleotide clones #Aminoacid clonotypes %Aminoacid clonotypes #In-frames %In-frames #Out-of-frames
glioma_TCRA_blood              155906                128505             0.8242467     155906          1              0
glioma_TCRA_brain               29469                 26328             0.8934134      29469          1              0
glioma_TCRB_blood              116909                105554             0.9028732     116909          1              0
glioma_TCRB_brain               24204                 22621             0.9345976      24204          1              0
healthy_TCRA_blood             320357                245471             0.7662420     320357          1              0
healthy_TCRA_brain                853                   813             0.9531067        853          1              0
healthy_TCRB_blood             345222                302915             0.8774499     345222          1              0
healthy_TCRB_brain                562                   544             0.9679715        562          1              0
                   %Out-of-frames Sum.reads Min.reads 1st Qu.reads Median.reads Mean.reads 3rd Qu.reads Max.reads Sum.UMIs
glioma_TCRA_blood               0   5469772         1            1            3   35.08378          9.0    945466  5469772
glioma_TCRA_brain               0    772065         1            1            3   26.19923         10.0     26062   772065
glioma_TCRB_blood               0   5156564         1            1            5   44.10750         17.0   1066219  5156564
glioma_TCRB_brain               0   1135126         1            1            6   46.89828         23.0     26608  1135126
healthy_TCRA_blood              0  12520964         1            1            3   39.08441         10.0   5655752 12520964
healthy_TCRA_brain              0   2592052         1            1            2 3038.74795        498.0    850624  2592052
healthy_TCRB_blood              0  11059604         1            1            3   32.03621         16.0   2571438 11059604
healthy_TCRB_brain              0    599398         1            1           43 1066.54448        512.5    150572   599398
                   Min.UMIs 1st Qu.UMIs Median.UMIs  Mean.UMIs 3rd Qu.UMIs Max.UMIs
glioma_TCRA_blood         1           1           3   35.08378         9.0   945466
glioma_TCRA_brain         1           1           3   26.19923        10.0    26062
glioma_TCRB_blood         1           1           5   44.10750        17.0  1066219
glioma_TCRB_brain         1           1           6   46.89828        23.0    26608
healthy_TCRA_blood        1           1           3   39.08441        10.0  5655752
healthy_TCRA_brain        1           1           2 3038.74795       498.0   850624
healthy_TCRB_blood        1           1           3   32.03621        16.0  2571438
healthy_TCRB_brain        1           1          43 1066.54448       512.5   150572

Stats about clonal proportion

# how many clones to make up 25% of the reads?
clonal_prop = clonal.proportion(cln_glio_list, 25)
# proportion taken by the top-10 clonotypes
top_clon_prop = top.proportion(cln_glio_list, 10) 
vis.top.proportions(cln_glio_list)
Using People as id variables

# clonotype expansion - what proportion of clonotypes fit a certain interval of abundance (as % of total clonotypes)
clonal_space = clonal.space.homeostasis(cln_glio_list)
pheatmap(t(clonal_space), cluster_rows = F)

CDR3 features

CDR3 (nucleotide) length distribution can give us clues about the clonotype diversity. A more skewed distribution implies greater representation of one or a few clonotypes.

vis.count.len(cln_glio_list[rownames(metadata[metadata$repertoire=="TCRA",])], 
              .col = "Read.count", .ncol = 2)

vis.count.len(cln_glio_list[rownames(metadata[metadata$repertoire=="TCRB",])], 
              .col = "Read.count", .ncol = 2)

Sequence logo for the CDR3 sequence - any evident motifs? The most correct way to run this analysis would be to have the CDR3 sequences aligned with each other since they can have different lengths. The following code serves mostly as an example on how to plot this in R.

plot_list = list()
for(n in names(cln_glio_list)){
  km <- get.kmers(cln_glio_list[[n]]$CDR3.amino.acid.sequence, .head = 100, .k = 16, .verbose = F)
  d <- kmer.profile(km)
  plot_list[[n]] = ggseqlogo(as.matrix(round(d[,-1]*100, 0)), method = 'prob')+ggtitle(n)
  print(plot_list[[n]])
}

Gene segment analysis

Gene segment usage

Plot gene usage (combinations) in heatmaps Normalization is required to account for library size, especially when comparing between samples

# V segment usage
BV_usage = geneUsage(cln_glio_list, HUMAN_TRBV, .norm = T)
Warning message:
In scan(file = file, what = what, sep = sep, quote = quote, dec = dec,  :
  EOF within quoted string
rownames(BV_usage) = BV_usage[,1]
BV_usage = BV_usage[,-1]
BV_usage = BV_usage[,!apply(BV_usage, 2, function(x) all(is.na(x)))]
AV_usage = geneUsage(cln_glio_list, HUMAN_TRAV, .norm = T)
rownames(AV_usage) = AV_usage[,1]
AV_usage = AV_usage[,-1]
AV_usage = AV_usage[,!apply(AV_usage, 2, function(x) all(is.na(x)))]
# J segment usage
BJ_usage = geneUsage(cln_glio_list, HUMAN_TRBJ, .norm = T)
rownames(BJ_usage) = BJ_usage[,1]
BJ_usage = BJ_usage[,-1]
BJ_usage = BJ_usage[,!apply(BJ_usage, 2, function(x) all(is.na(x)))]
AJ_usage = geneUsage(cln_glio_list, HUMAN_TRAJ, .norm = T)
rownames(AJ_usage) = AJ_usage[,1]
AJ_usage = AJ_usage[,-1]
AJ_usage = AJ_usage[,!apply(AJ_usage, 2, function(x) all(is.na(x)))]
both_usage = list()
for(n in names(cln_glio_list)){
  if(metadata[n,"repertoire"]=="TCRB"){
    both_usage[[n]] = geneUsage(cln_glio_list[[n]], list(HUMAN_TRBV, HUMAN_TRBJ), .norm = T)
  } else{
    both_usage[[n]] = geneUsage(cln_glio_list[[n]], list(HUMAN_TRAV, HUMAN_TRAJ), .norm = T)
  }
}
# how are the different samples distinguished by segment usage?
pheatmap::pheatmap(t(BV_usage))

pheatmap::pheatmap(t(AV_usage))

pheatmap::pheatmap(t(BJ_usage))

pheatmap::pheatmap(t(AJ_usage))

# what are the segment combination biases per sample?
pheatmap::pheatmap(t(both_usage$glioma_TCRB_blood), main = "glioma_TCRB_blood")

pheatmap::pheatmap(t(both_usage$glioma_TCRB_brain), main = "glioma_TCRB_brain")

pheatmap::pheatmap(t(both_usage$healthy_TCRB_blood), main = "healthy_TCRB_blood")

pheatmap::pheatmap(t(both_usage$healthy_TCRB_brain), main = "healthy_TCRB_brain")

Gene segment comparison by information distance

Information on diversity measures: https://en.wikipedia.org/wiki/Diversity_index Entropy - measure of how unpredictable the data is. The larger the entropy, the more diverse (in this case the repertoire). Compute repertoire entropy

# V segment usage entropy
BV_entropy = entropy.seg(cln_glio_list[rownames(metadata[metadata$repertoire=="TCRB",])], HUMAN_TRBV)
AV_entropy = entropy.seg(cln_glio_list[rownames(metadata[metadata$repertoire=="TCRA",])], HUMAN_TRAV)
Note: difference between the sum of the input vector and 1 is -1.11022302462516e-16, which may be caused by internal R subroutines and may not affect the result at all.
# J segment usage entropy
BJ_entropy = entropy.seg(cln_glio_list[rownames(metadata[metadata$repertoire=="TCRB",])], HUMAN_TRBJ)
AJ_entropy = entropy.seg(cln_glio_list[rownames(metadata[metadata$repertoire=="TCRA",])], HUMAN_TRAJ)
# plot
TCRB_df = data.frame(V_seg = BV_entropy,
                     J_seg = BJ_entropy)
TCRB_df = merge(TCRB_df, metadata, by = 0)
ggplot(TCRB_df, aes(x = V_seg, y = J_seg, colour = condition, shape = tissue))+
  geom_point()+
  scale_shape_manual(values = c(2, 19))+
  ggtitle("TCR beta gene segment entropy")+
  theme_classic()+
  theme(aspect.ratio = 1)

TCRA_df = data.frame(V_seg = AV_entropy,
                     J_seg = AJ_entropy)
TCRA_df = merge(TCRA_df, metadata, by = 0)
ggplot(TCRA_df, aes(x = V_seg, y = J_seg, colour = condition, shape = tissue))+
  geom_point()+
  scale_shape_manual(values = c(2, 19))+
  ggtitle("TCR alpha gene segment entropy")+
  theme_classic()+
  theme(aspect.ratio = 1)

Other measures of repertoire diversity

# True diversity - the most fundamental measure of diversity, a few others are derived from this one
repDiversity(cln_glio_list[rownames(metadata[metadata$repertoire=="TCRB",])], 'div', 'read.count', 
             .norm = T, .do.norm = T)
Warning! Sum of the input vector is NOT equal to 1. Function may produce incorrect results.
Note: difference between the sum of the input vector and 1 is -9.99200722162641e-16, which may be caused by internal R subroutines and may not affect the result at all.

Warning! Sum of the input vector is NOT equal to 1. Function may produce incorrect results.
Note: difference between the sum of the input vector and 1 is -1.11022302462516e-16, which may be caused by internal R subroutines and may not affect the result at all.

Warning! Sum of the input vector is NOT equal to 1. Function may produce incorrect results.
Note: difference between the sum of the input vector and 1 is -2.33146835171283e-15, which may be caused by internal R subroutines and may not affect the result at all.
 glioma_TCRB_blood  glioma_TCRB_brain healthy_TCRB_blood healthy_TCRB_brain 
          7.152546          89.817975           6.193750           5.621460 
repDiversity(cln_glio_list[rownames(metadata[metadata$repertoire=="TCRA",])], 'div', 'read.count', 
             .norm = T, .do.norm = T)
Warning! Sum of the input vector is NOT equal to 1. Function may produce incorrect results.
Note: difference between the sum of the input vector and 1 is 8.88178419700125e-16, which may be caused by internal R subroutines and may not affect the result at all.

Warning! Sum of the input vector is NOT equal to 1. Function may produce incorrect results.
Note: difference between the sum of the input vector and 1 is -1.11022302462516e-16, which may be caused by internal R subroutines and may not affect the result at all.

Warning! Sum of the input vector is NOT equal to 1. Function may produce incorrect results.
Note: difference between the sum of the input vector and 1 is -9.99200722162641e-16, which may be caused by internal R subroutines and may not affect the result at all.
 glioma_TCRA_blood  glioma_TCRA_brain healthy_TCRA_blood healthy_TCRA_brain 
          8.823326          64.874693           2.700438           4.008461 
# Gini coefficient - measure of inequality in the data (the larger the more unequal) (very used in economy)
repDiversity(cln_glio_list[rownames(metadata[metadata$repertoire=="TCRB",])], 'gini', 'read.prop', 
             .norm = T, .do.norm = T)
 glioma_TCRB_blood  glioma_TCRB_brain healthy_TCRB_blood healthy_TCRB_brain 
         0.8930059          0.8588808          0.8631150          0.8813563 
repDiversity(cln_glio_list[rownames(metadata[metadata$repertoire=="TCRA",])], 'gini', 'read.prop', 
             .norm = T, .do.norm = T)
 glioma_TCRA_blood  glioma_TCRA_brain healthy_TCRA_blood healthy_TCRA_brain 
         0.9126299          0.8690689          0.9115846          0.9512683 

Compute divergence between samples Jensen-Shannon divergence - similarity between two probability distributions. Related to the Kullback–Leibler divergence, a measure of “relative entropy”.

js_list = list(
  BV_js = js.div.seg(cln_glio_list[rownames(metadata[metadata$repertoire=="TCRB",])], 
                     HUMAN_TRBV, .verbose = F),
  AV_js = js.div.seg(cln_glio_list[rownames(metadata[metadata$repertoire=="TCRA",])], 
                     HUMAN_TRAV, .verbose = F),
  
  BJ_js = js.div.seg(cln_glio_list[rownames(metadata[metadata$repertoire=="TCRB",])], 
                     HUMAN_TRBJ, .verbose = F),
  AJ_js = js.div.seg(cln_glio_list[rownames(metadata[metadata$repertoire=="TCRA",])], 
                     HUMAN_TRAJ, .verbose = F)
)
Warning! Sum of the input vector is NOT equal to 1. Function may produce incorrect results.
 To fix this try to set .do.norm = TRUE in the function's parameters.
Note: difference between the sum of the input vector and 1 is -1.11022302462516e-16, which may be caused by internal R subroutines and may not affect the result at all.

Warning! Sum of the input vector is NOT equal to 1. Function may produce incorrect results.
 To fix this try to set .do.norm = TRUE in the function's parameters.
Note: difference between the sum of the input vector and 1 is -1.11022302462516e-16, which may be caused by internal R subroutines and may not affect the result at all.

Warning! Sum of the input vector is NOT equal to 1. Function may produce incorrect results.
 To fix this try to set .do.norm = TRUE in the function's parameters.
Note: difference between the sum of the input vector and 1 is -1.11022302462516e-16, which may be caused by internal R subroutines and may not affect the result at all.

Warning! Sum of the input vector is NOT equal to 1. Function may produce incorrect results.
 To fix this try to set .do.norm = TRUE in the function's parameters.
Note: difference between the sum of the input vector and 1 is -1.11022302462516e-16, which may be caused by internal R subroutines and may not affect the result at all.

Warning! Sum of the input vector is NOT equal to 1. Function may produce incorrect results.
 To fix this try to set .do.norm = TRUE in the function's parameters.
Note: difference between the sum of the input vector and 1 is -1.11022302462516e-16, which may be caused by internal R subroutines and may not affect the result at all.

Warning! Sum of the input vector is NOT equal to 1. Function may produce incorrect results.
 To fix this try to set .do.norm = TRUE in the function's parameters.
Note: difference between the sum of the input vector and 1 is -1.11022302462516e-16, which may be caused by internal R subroutines and may not affect the result at all.

Warning! Sum of the input vector is NOT equal to 1. Function may produce incorrect results.
 To fix this try to set .do.norm = TRUE in the function's parameters.
Note: difference between the sum of the input vector and 1 is -1.11022302462516e-16, which may be caused by internal R subroutines and may not affect the result at all.

Warning! Sum of the input vector is NOT equal to 1. Function may produce incorrect results.
 To fix this try to set .do.norm = TRUE in the function's parameters.
Note: difference between the sum of the input vector and 1 is -1.11022302462516e-16, which may be caused by internal R subroutines and may not affect the result at all.

Warning! Sum of the input vector is NOT equal to 1. Function may produce incorrect results.
 To fix this try to set .do.norm = TRUE in the function's parameters.
Note: difference between the sum of the input vector and 1 is -1.11022302462516e-16, which may be caused by internal R subroutines and may not affect the result at all.

Warning! Sum of the input vector is NOT equal to 1. Function may produce incorrect results.
 To fix this try to set .do.norm = TRUE in the function's parameters.
Note: difference between the sum of the input vector and 1 is -1.11022302462516e-16, which may be caused by internal R subroutines and may not affect the result at all.

Warning! Sum of the input vector is NOT equal to 1. Function may produce incorrect results.
 To fix this try to set .do.norm = TRUE in the function's parameters.
Note: difference between the sum of the input vector and 1 is -1.11022302462516e-16, which may be caused by internal R subroutines and may not affect the result at all.

Warning! Sum of the input vector is NOT equal to 1. Function may produce incorrect results.
 To fix this try to set .do.norm = TRUE in the function's parameters.
Note: difference between the sum of the input vector and 1 is -1.11022302462516e-16, which may be caused by internal R subroutines and may not affect the result at all.

Warning! Sum of the input vector is NOT equal to 1. Function may produce incorrect results.
 To fix this try to set .do.norm = TRUE in the function's parameters.
Note: difference between the sum of the input vector and 1 is -1.11022302462516e-16, which may be caused by internal R subroutines and may not affect the result at all.
for(n in names(js_list)){pheatmap::pheatmap(js_list[[n]], main = n)}

Dimensionality reduction by PCA with gene usage - how do different samples cluster based on their expression of VJ segments? This case will not be very informative as we are only looking at 4 samples.

# use AJ and AV combination
pca_2d = pca.segments.2D(cln_glio_list[rownames(metadata[metadata$repertoire=="TCRA",])],
                              .genes =  list(HUMAN_TRAV, HUMAN_TRAJ), .do.plot = F)
In prcomp.default(do.call(rbind, .data), ...) :
 extra argument ‘.genes’ will be disregarded
plot_df = merge(pca_2d$x, metadata, by = 0)
ggplot(plot_df, aes(x = PC1, y = PC2, colour = condition, shape = tissue))+
  geom_point(size = 2)+
  scale_shape_manual(values = c(19, 2))+
  ggtitle("TCR alpha PCA")+
  theme_classic()+
  theme(aspect.ratio = 1,
        legend.position = "right")

# use BJ and BV combination
pca_2d = pca.segments.2D(cln_glio_list[rownames(metadata[metadata$repertoire=="TCRB",])],
                              .genes =  list(HUMAN_TRBV, HUMAN_TRBJ), .do.plot = F)
number of columns of result is not a multiple of vector length (arg 1)In prcomp.default(do.call(rbind, .data), ...) :
 extra argument ‘.genes’ will be disregarded
plot_df = merge(pca_2d$x, metadata, by = 0)
ggplot(plot_df, aes(x = PC1, y = PC2, colour = condition, shape = tissue))+
  geom_point(size = 2)+
  scale_shape_manual(values = c(19, 2))+
  ggtitle("TCR beta PCA")+
  theme_classic()+
  theme(aspect.ratio = 1,
        legend.position = "right")

Comparing repertoires

Compute a pairwise normalised number of shared clonotypes and plot a heatmap of them Normalisation is important to avoid bisases by different library size and clonotype capture

# TCRA
ovelap_A_nuc_exact <- repOverlap(cln_glio_list[rownames(metadata[metadata$repertoire=="TCRA",])], 
                                    "exact", .norm = T, .seq = "nuc")

  |                                                                                                                          
  |                                                                                                                    |   0%
  |                                                                                                                          
  |============                                                                                                        |  10%
  |                                                                                                                          
  |=======================                                                                                             |  20%
  |                                                                                                                          
  |===================================                                                                                 |  30%
  |                                                                                                                          
  |==============================================                                                                      |  40%
  |                                                                                                                          
  |==========================================================                                                          |  50%
  |                                                                                                                          
  |======================================================================                                              |  60%
  |                                                                                                                          
  |=================================================================================                                   |  70%
  |                                                                                                                          
  |=============================================================================================                       |  80%
  |                                                                                                                          
  |========================================================================================================            |  90%
  |                                                                                                                          
  |====================================================================================================================| 100%
ovelap_A_aa_exact <- repOverlap(cln_glio_list[rownames(metadata[metadata$repertoire=="TCRA",])], 
                                   "exact", .norm = T, .seq = "aa")

  |                                                                                                                          
  |                                                                                                                    |   0%
  |                                                                                                                          
  |============                                                                                                        |  10%
  |                                                                                                                          
  |=======================                                                                                             |  20%
  |                                                                                                                          
  |===================================                                                                                 |  30%
  |                                                                                                                          
  |==============================================                                                                      |  40%
  |                                                                                                                          
  |==========================================================                                                          |  50%
  |                                                                                                                          
  |======================================================================                                              |  60%
  |                                                                                                                          
  |=================================================================================                                   |  70%
  |                                                                                                                          
  |=============================================================================================                       |  80%
  |                                                                                                                          
  |========================================================================================================            |  90%
  |                                                                                                                          
  |====================================================================================================================| 100%
vis.heatmap(ovelap_A_nuc_exact, .text = F, .title = "TCRA clonotype overlap\nNucleotide Sequence")

vis.heatmap(ovelap_A_aa_exact, .text = F, .title = "TCRA clonotype overlap\nAminoacid Sequence")

# TCRB
ovelap_B_nuc_exact <- repOverlap(cln_glio_list[rownames(metadata[metadata$repertoire=="TCRB",])], 
                                    "exact", .norm = T, .seq = "nuc")

  |                                                                                                                          
  |                                                                                                                    |   0%
  |                                                                                                                          
  |============                                                                                                        |  10%
  |                                                                                                                          
  |=======================                                                                                             |  20%
  |                                                                                                                          
  |===================================                                                                                 |  30%
  |                                                                                                                          
  |==============================================                                                                      |  40%
  |                                                                                                                          
  |==========================================================                                                          |  50%
  |                                                                                                                          
  |======================================================================                                              |  60%
  |                                                                                                                          
  |=================================================================================                                   |  70%
  |                                                                                                                          
  |=============================================================================================                       |  80%
  |                                                                                                                          
  |========================================================================================================            |  90%
  |                                                                                                                          
  |====================================================================================================================| 100%
ovelap_B_aa_exact <- repOverlap(cln_glio_list[rownames(metadata[metadata$repertoire=="TCRB",])], 
                                   "exact", .norm = T, .seq = "aa")

  |                                                                                                                          
  |                                                                                                                    |   0%
  |                                                                                                                          
  |============                                                                                                        |  10%
  |                                                                                                                          
  |=======================                                                                                             |  20%
  |                                                                                                                          
  |===================================                                                                                 |  30%
  |                                                                                                                          
  |==============================================                                                                      |  40%
  |                                                                                                                          
  |==========================================================                                                          |  50%
  |                                                                                                                          
  |======================================================================                                              |  60%
  |                                                                                                                          
  |=================================================================================                                   |  70%
  |                                                                                                                          
  |=============================================================================================                       |  80%
  |                                                                                                                          
  |========================================================================================================            |  90%
  |                                                                                                                          
  |====================================================================================================================| 100%
vis.heatmap(ovelap_B_nuc_exact, .text = F, .title = "TCRB clonotype overlap\nNucleotide Sequence")

vis.heatmap(ovelap_B_aa_exact, .text = F, .title = "TCRB clonotype overlap\nAminoacid Sequence")

Top cross - number of shared clonotypes among those most highly represented Important because these tend to be the most relevant forms of the TCR

tcra_topcross <- top.cross(.data = cln_glio_list[rownames(metadata[metadata$repertoire=="TCRA",])], 
                     .n = seq(500, 10000, 500), .verbose = F, .norm = T)
top.cross.plot(tcra_topcross)

tcrb_topcross <- top.cross(.data = cln_glio_list[rownames(metadata[metadata$repertoire=="TCRB",])], 
                     .n = seq(500, 10000, 500), .verbose = F, .norm = T)
top.cross.plot(tcrb_topcross)

Shared repertoire - number of shared clonotypes for each repertoire for each degree of sharing (i.e., number of samples in which indicated amount of clones have been found).

shared_A_rep = shared.repertoire(cln_glio_list[rownames(metadata[metadata$repertoire=="TCRA",])], 
                                 .min.ppl = 2, .clear = T)
Aggregating sequences...

  |                                                                                                                          
  |                                                                                                                    |   0%
  |                                                                                                                          
  |=============================                                                                                       |  25%
  |                                                                                                                          
  |==========================================================                                                          |  50%
  |                                                                                                                          
  |=======================================================================================                             |  75%
  |                                                                                                                          
  |====================================================================================================================| 100%
Merging data tables...

  |                                                                                                                          
  |                                                                                                                    |   0%
  |                                                                                                                          
  |=======================================                                                                             |  33%
  |                                                                                                                          
  |=============================================================================                                       |  67%
  |                                                                                                                          
  |====================================================================================================================| 100%
shared.representation(shared_A_rep)
  glioma_TCRA_blood glioma_TCRA_brain healthy_TCRA_blood healthy_TCRA_brain
1                 0                 0                  0                  0
2             22108             10135              14790                231
3              2803              2767               2805                 43
4                11                11                 11                 11
shared_B_rep = shared.repertoire(cln_glio_list[rownames(metadata[metadata$repertoire=="TCRB",])], 
                                 .min.ppl = 2, .clear = T)
Aggregating sequences...

  |                                                                                                                          
  |                                                                                                                    |   0%
  |                                                                                                                          
  |=============================                                                                                       |  25%
  |                                                                                                                          
  |==========================================================                                                          |  50%
  |                                                                                                                          
  |=======================================================================================                             |  75%
  |                                                                                                                          
  |====================================================================================================================| 100%
Merging data tables...

  |                                                                                                                          
  |                                                                                                                    |   0%
  |                                                                                                                          
  |=======================================                                                                             |  33%
  |                                                                                                                          
  |=============================================================================                                       |  67%
  |                                                                                                                          
  |====================================================================================================================| 100%
shared.representation(shared_B_rep)
  glioma_TCRB_blood glioma_TCRB_brain healthy_TCRB_blood healthy_TCRB_brain
1                 0                 0                  0                  0
2              7193              6423               1245                185
3                91                91                 91                  0
4                 0                 0                  0                  0

Shared repertoire mutation networks - how do the different clonotypes relate to each other by sequence similarity?

# TCRBA
shared_A_rep_top = shared.repertoire(cln_glio_list[rownames(metadata[metadata$repertoire=="TCRA",])], 
                                     .min.ppl = 2, .clear = T, .head = 2000)
Aggregating sequences...

  |                                                                                                                          
  |                                                                                                                    |   0%
  |                                                                                                                          
  |=============================                                                                                       |  25%
  |                                                                                                                          
  |==========================================================                                                          |  50%
  |                                                                                                                          
  |=======================================================================================                             |  75%
  |                                                                                                                          
  |====================================================================================================================| 100%
Merging data tables...

  |                                                                                                                          
  |                                                                                                                    |   0%
  |                                                                                                                          
  |=======================================                                                                             |  33%
  |                                                                                                                          
  |=============================================================================                                       |  67%
  |                                                                                                                          
  |====================================================================================================================| 100%
G_A <- mutation.network(shared_A_rep_top, .max.errors = 3)
## plotting attributes
V(G_A)$color = metadata[metadata$repertoire=="TCRA","condition"]
number of items to replace is not a multiple of replacement length
V(G_A)$shape = metadata[metadata$repertoire=="TCRA","tissue"]
number of items to replace is not a multiple of replacement length
## network layout
l_A = igraph::layout.fruchterman.reingold(G_A)
# TCRB
shared_B_rep_top = shared.repertoire(cln_glio_list[rownames(metadata[metadata$repertoire=="TCRB",])], 
                                     .min.ppl = 2, .clear = T, .head = 2000)
Aggregating sequences...

  |                                                                                                                          
  |                                                                                                                    |   0%
  |                                                                                                                          
  |=============================                                                                                       |  25%
  |                                                                                                                          
  |==========================================================                                                          |  50%
  |                                                                                                                          
  |=======================================================================================                             |  75%
  |                                                                                                                          
  |====================================================================================================================| 100%
Merging data tables...

  |                                                                                                                          
  |                                                                                                                    |   0%
  |                                                                                                                          
  |=======================================                                                                             |  33%
  |                                                                                                                          
  |=============================================================================                                       |  67%
  |                                                                                                                          
  |====================================================================================================================| 100%
G_B <- mutation.network(shared_B_rep_top, .max.errors = 3)
## plotting attributes
V(G_B)$color = metadata[metadata$repertoire=="TCRB","condition"]
number of items to replace is not a multiple of replacement length
V(G_B)$shape = metadata[metadata$repertoire=="TCRB","tissue"]
number of items to replace is not a multiple of replacement length
## network layout
l_B = igraph::layout.fruchterman.reingold(G_B)

Making the actual network plot More info: https://plot.ly/r/network-graphs/

# ploting function
makePlotlyNetwork = function(igraph_obj, layout_net, title_net){
  vs <- V(igraph_obj)
  es <- as.data.frame(get.edgelist(igraph_obj))
  
  Nv <- length(vs)
  Ne <- length(es[1]$V1)
  
  Xn <- layout_net[,1]
  Yn <- layout_net[,2]
  
  network <- plotly::plot_ly(x = ~Xn, y = ~Yn, mode = "markers", text = vs$label, 
                             hoverinfo = "text", color = vs$color, symbol = vs$shape, 
                             symbols = c("19", "2"), colors = c("#FF0006", "#0080FF"))
  edge_shapes <- list()
  for(i in 1:Ne) {
    v0 <- es[i,]$V1
    v1 <- es[i,]$V2
  
    edge_shape = list(
      type = "line",
      line = list(color = "#030303", width = 0.3),
      x0 = Xn[v0],
      y0 = Yn[v0],
      x1 = Xn[v1],
      y1 = Yn[v1]
    )
  
    edge_shapes[[i]] <- edge_shape
  }
  
  axis <- list(title = "", showgrid = FALSE, showticklabels = FALSE, zeroline = FALSE)
  
  p <- plotly::layout(
    network,
    title = title_net,
    shapes = edge_shapes,
    xaxis = axis,
    yaxis = axis
  )
}
# make the plots
p = makePlotlyNetwork(G_A, l_A, "TCRA mutation graph")
p
No trace type specified:
  Based on info supplied, a 'scatter' trace seems appropriate.
  Read more about this trace type -> https://plot.ly/r/reference/#scatter
No trace type specified:
  Based on info supplied, a 'scatter' trace seems appropriate.
  Read more about this trace type -> https://plot.ly/r/reference/#scatter

p = makePlotlyNetwork(G_B, l_B, "TCRB mutation graph")
p
No trace type specified:
  Based on info supplied, a 'scatter' trace seems appropriate.
  Read more about this trace type -> https://plot.ly/r/reference/#scatter
No trace type specified:
  Based on info supplied, a 'scatter' trace seems appropriate.
  Read more about this trace type -> https://plot.ly/r/reference/#scatter
LS0tCnRpdGxlOiAiQnVsayBUQ1IgcmVwZXJ0b2lyZSBhbmFseXNpcyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKCgoKTG9hZCBsaWJyYXJpZXMKCmBgYHtyfQpsaWJyYXJ5KHRjUiwgcXVpZXRseSA9IFQpICMgbGlicmFyeSB0byBwcm9jZXNzIFRDUiByZXBlcnRvaXJlIHNhbXBsZXMKbGlicmFyeShnZ3NlcWxvZ28pICMgcGxvdCBsb2dvcyBvZiBzZXF1ZW5jZSBtb3RpZnMKbGlicmFyeShwaGVhdG1hcCkgIyBwbG90IChwcmV0dHkpIGhlYXRtYXBzCmxpYnJhcnkocGxvdGx5KSAjIGludGVyYWN0aXZlIHBsb3R0aW5nCmBgYAoKCgojIERhdGEgcHJvY2Vzc2luZwpFeGFtcGxlIGNvbW1hbmRzIGZvciB1c2luZyBNaXhDUiB0byBwcm9jZXNzIHJlcGVydG9pcmUgc2VxdWVuY2luZyBkYXRhClBhcmFsbGVsaXppbmcgYW5kIG11bHRpdGhyZWFkaW5nIHRoZXNlIHN0ZXBzIGlzIGhpZ2hseSBhZHZpc2VkLiBTaW5nbGUgdGhyZWFkZWQgYW5hbHlzaXMgY2FuIHJ1biBmb3IgfjIgaG91cnMKTW9yZSBpbmZvIGluOiBodHRwczovL2dpdGh1Yi5jb20vbWlsYWJvcmF0b3J5L21peGNyCgpgYGB7YmFzaCwgZXZhbCA9IEZBTFNFfQojIGFsaWduIHdpdGggVENSIHJlZmVyZW5jZQojIyBhbGlnbm1lbnQgY2FuIGJlIGRvbmUgb25seSBhZ2FpbnN0IHJlZmVyZW5jZSBvZiBpbnRlcmVzdCAoaWYgdGVzdGluZyBUQ1IgYmV0YSByZXBlcnRvaXJlIG9ubHkgYWxpZ24gYWdhaW5zdCB0aG9zZSkKbWl4Y3IgYWxpZ24gLXIgc2FtcGxlX1RDUkEubG9nIC10IDEwIC1mIC1jIFRSQSAtcyBocyBzYW1wbGVfVENSQV8xLmZhc3RxIHNhbXBsZV9UQ1JBXzEuZmFzdHEgc2FtcGxlX1RDUkEudmRqY2EKbWl4Y3IgYWxpZ24gLXIgc2FtcGxlX1RDUkIubG9nIC10IDEwIC1mIC1jIFRSQiAtcyBocyBzYW1wbGVfVENSQl8xLmZhc3RxIHNhbXBsZV9UQ1JCXzEuZmFzdHEgc2FtcGxlX1RDUkIudmRqY2EKCiMgVENSIHNlcXVlbmNlIGFzc2VtYmx5Cm1peGNyIGFzc2VtYmxlIC1yIHNhbXBsZV9UQ1JBLmxvZyAtdCAxMCAtZiBzYW1wbGVfVENSQS52ZGpjYSBzYW1wbGVfVENSQS5jbG5zCm1peGNyIGFzc2VtYmxlIC1yIHNhbXBsZV9UQ1JCLmxvZyAtdCAxMCAtZiBzYW1wbGVfVENSQS52ZGpjYSBzYW1wbGVfVENSQi5jbG5zCgojIGV4cG9ydCBvdXRwdXQgdG8gYSByZWFkYWJsZSB0YWJsZSBmb3JtYXQKbWl4Y3IgZXhwb3J0Q2xvbmVzIHNhbXBsZV9UQ1JBLmNsbnMgc2FtcGxlX1RDUkEudHh0Cm1peGNyIGV4cG9ydENsb25lcyBzYW1wbGVfVENSQi5jbG5zIHNhbXBsZV9UQ1JCLnR4dApgYGAKCgoKIyBEYXRhIHByZXBhcmF0aW9uCkxvYWQgZGF0YSAoYXBwcm94LiA1bWluIHdpdGggdGhpcyBleGFtcGxlKQoKYGBge3IsIGV2YWwgPSBGQUxTRX0Kc3lzdGVtKCJnemlwIC1kIHRlc3RfZGF0YS8qZ3oiKSAjIHVuemlwIHRlc3QgZmlsZXMKY2xuX3R4dCA9IGxpc3QuZmlsZXMocGF0aCA9ICIuL3Rlc3RfZGF0YS8iLCBwYXR0ZXJuID0gIi50eHQiKSAjIGxpc3QgYWxsIHRlc3QgZmlsZXMKY2xuX2dsaW9fcmF3ID0gbGFwcGx5KHBhc3RlMCgiLi90ZXN0X2RhdGEvIiwgY2xuX3R4dCksIAogICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpe3BhcnNlLm1peGNyKHgpfSkgIyBwYXJzZSB0ZXN0IGZpbGVzCm5hbWVzKGNsbl9nbGlvX3JhdykgPSBzYXBwbHkoc3Ryc3BsaXQoY2xuX3R4dCwgIi4iLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB4WzFdKQoKIyBnZXQgc2FtcGxlIG1ldGFkYXRhIGZyb20gZmlsZSBuYW1lcwptZXRhZGF0YSA9IGRhdGEuZnJhbWUodChzYXBwbHkoc3Ryc3BsaXQobmFtZXMoY2xuX2dsaW9fcmF3KSwgIl8iKSwgcmJpbmQpKSwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCnJvd25hbWVzKG1ldGFkYXRhKSA9IG5hbWVzKGNsbl9nbGlvX3JhdykKY29sbmFtZXMobWV0YWRhdGEpID0gYygiY29uZGl0aW9uIiwgInJlcGVydG9pcmUiLCAidGlzc3VlIikKCiNzYXZlKG1ldGFkYXRhLCBjbG5fZ2xpb19yYXcsIGZpbGUgPSAiYWxsX3Rlc3RfZGF0YS5SRGF0YSIpCmBgYAoKCgpMb2FkIC5SRGF0YSB3aXRoIGFsbCBuZWVkZWQgb2JqZWN0cyAoZmFzdGVyIHRoYW4gcHJldmlvdXMgY29kZSkKCmBgYHtyfQpsb2FkKCJhbGxfdGVzdF9kYXRhLlJEYXRhIikKYGBgCgoKCgpPdXQgb2YgZnJhbWUgVENSIHNlcXVlbmNlcyBhcmUgbm9uIGZ1bmN0aW9uYWwgYnV0IGNhbiBzdGlsbCBhcHBlYXIuIEluIGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcG1jL2FydGljbGVzL1BNQzM4NzIyOTkvOiAiLi4ub3V0LW9mLWZyYW1lIChpLmUuLCBub24tZnVuY3Rpb25hbCBhbmQgdGh1cyBub3Qgc3ViamVjdGVkIHRvIHNlbGVjdGlvbikgYW5kIGluLWZyYW1lIFRDUiBiZXRhIHJlcGVydG9pcmVzLCB3ZSBhbHNvIHNob3cgdGhlIGV4dGVudCBvZiB0aGUgaW1wYWN0IG9mIHRoeW1pYyBzZWxlY3Rpb24gYW5kIHRoZSBjb21tb24gdHJlbmRzIGluIGhvdyB0aGlzIHByb2Nlc3Mgc2hhcGVzIGluZGl2aWR1YWwgcmVwZXJ0b2lyZXMuIiIKRmlsdGVyaW5nIGNsb25vdHlwZXMgaW4gZnJhbWUvbm90IGluIGZyYW1lOgoKYGBge3J9CmNsbl9nbGlvX2xpc3QgPC0gZ2V0LmluZnJhbWVzKGNsbl9nbGlvX3JhdykKYGBgCgoKCiMgQmFzaWMgc3RhdHMgYWJvdXQgdGhlIGRhdGFzZXQKQ29tcHV0ZSBiYXNpYyBzdGF0cwoKYGBge3J9CmJhc2ljX3N0YXRzID0gY2xvbmVzZXQuc3RhdHMoY2xuX2dsaW9fbGlzdCkKYmFzaWNfc3RhdHMKYGBgCgoKClN0YXRzIGFib3V0IGNsb25hbCBwcm9wb3J0aW9uCgpgYGB7cn0KIyBob3cgbWFueSBjbG9uZXMgdG8gbWFrZSB1cCAyNSUgb2YgdGhlIHJlYWRzPwpjbG9uYWxfcHJvcCA9IGNsb25hbC5wcm9wb3J0aW9uKGNsbl9nbGlvX2xpc3QsIDI1KQoKIyBwcm9wb3J0aW9uIHRha2VuIGJ5IHRoZSB0b3AtMTAgY2xvbm90eXBlcwp0b3BfY2xvbl9wcm9wID0gdG9wLnByb3BvcnRpb24oY2xuX2dsaW9fbGlzdCwgMTApIAp2aXMudG9wLnByb3BvcnRpb25zKGNsbl9nbGlvX2xpc3QpCgojIGNsb25vdHlwZSBleHBhbnNpb24gLSB3aGF0IHByb3BvcnRpb24gb2YgY2xvbm90eXBlcyBmaXQgYSBjZXJ0YWluIGludGVydmFsIG9mIGFidW5kYW5jZSAoYXMgJSBvZiB0b3RhbCBjbG9ub3R5cGVzKQpjbG9uYWxfc3BhY2UgPSBjbG9uYWwuc3BhY2UuaG9tZW9zdGFzaXMoY2xuX2dsaW9fbGlzdCkKcGhlYXRtYXAodChjbG9uYWxfc3BhY2UpLCBjbHVzdGVyX3Jvd3MgPSBGKQpgYGAKCgoKIyBDRFIzIGZlYXR1cmVzCkNEUjMgKG51Y2xlb3RpZGUpIGxlbmd0aCBkaXN0cmlidXRpb24gY2FuIGdpdmUgdXMgY2x1ZXMgYWJvdXQgdGhlIGNsb25vdHlwZSBkaXZlcnNpdHkuIEEgbW9yZSBza2V3ZWQgZGlzdHJpYnV0aW9uIGltcGxpZXMgZ3JlYXRlciByZXByZXNlbnRhdGlvbiBvZiBvbmUgb3IgYSBmZXcgY2xvbm90eXBlcy4KCmBgYHtyfQp2aXMuY291bnQubGVuKGNsbl9nbGlvX2xpc3Rbcm93bmFtZXMobWV0YWRhdGFbbWV0YWRhdGEkcmVwZXJ0b2lyZT09IlRDUkEiLF0pXSwgCiAgICAgICAgICAgICAgLmNvbCA9ICJSZWFkLmNvdW50IiwgLm5jb2wgPSAyKQp2aXMuY291bnQubGVuKGNsbl9nbGlvX2xpc3Rbcm93bmFtZXMobWV0YWRhdGFbbWV0YWRhdGEkcmVwZXJ0b2lyZT09IlRDUkIiLF0pXSwgCiAgICAgICAgICAgICAgLmNvbCA9ICJSZWFkLmNvdW50IiwgLm5jb2wgPSAyKQpgYGAKCgoKU2VxdWVuY2UgbG9nbyBmb3IgdGhlIENEUjMgc2VxdWVuY2UgLSBhbnkgZXZpZGVudCBtb3RpZnM/ClRoZSBtb3N0IGNvcnJlY3Qgd2F5IHRvIHJ1biB0aGlzIGFuYWx5c2lzIHdvdWxkIGJlIHRvIGhhdmUgdGhlIENEUjMgc2VxdWVuY2VzIGFsaWduZWQgd2l0aCBlYWNoIG90aGVyIHNpbmNlIHRoZXkgY2FuIGhhdmUgZGlmZmVyZW50IGxlbmd0aHMuIFRoZSBmb2xsb3dpbmcgY29kZSBzZXJ2ZXMgbW9zdGx5IGFzIGFuIGV4YW1wbGUgb24gaG93IHRvIHBsb3QgdGhpcyBpbiBSLgoKYGBge3J9CnBsb3RfbGlzdCA9IGxpc3QoKQpmb3IobiBpbiBuYW1lcyhjbG5fZ2xpb19saXN0KSl7CiAga20gPC0gZ2V0LmttZXJzKGNsbl9nbGlvX2xpc3RbW25dXSRDRFIzLmFtaW5vLmFjaWQuc2VxdWVuY2UsIC5oZWFkID0gMTAwLCAuayA9IDE2LCAudmVyYm9zZSA9IEYpCiAgZCA8LSBrbWVyLnByb2ZpbGUoa20pCiAgcGxvdF9saXN0W1tuXV0gPSBnZ3NlcWxvZ28oYXMubWF0cml4KHJvdW5kKGRbLC0xXSoxMDAsIDApKSwgbWV0aG9kID0gJ3Byb2InKSArIGdndGl0bGUobikKICBwcmludChwbG90X2xpc3RbW25dXSkKfQpgYGAKCgoKIyBHZW5lIHNlZ21lbnQgYW5hbHlzaXMKIyMgR2VuZSBzZWdtZW50IHVzYWdlClBsb3QgZ2VuZSB1c2FnZSAoY29tYmluYXRpb25zKSBpbiBoZWF0bWFwcwpOb3JtYWxpemF0aW9uIGlzIHJlcXVpcmVkIHRvIGFjY291bnQgZm9yIGxpYnJhcnkgc2l6ZSwgZXNwZWNpYWxseSB3aGVuIGNvbXBhcmluZyBiZXR3ZWVuIHNhbXBsZXMKCmBgYHtyfQojIFYgc2VnbWVudCB1c2FnZQpCVl91c2FnZSA9IGdlbmVVc2FnZShjbG5fZ2xpb19saXN0LCBIVU1BTl9UUkJWLCAubm9ybSA9IFQpCnJvd25hbWVzKEJWX3VzYWdlKSA9IEJWX3VzYWdlWywxXQpCVl91c2FnZSA9IEJWX3VzYWdlWywtMV0KQlZfdXNhZ2UgPSBCVl91c2FnZVssIWFwcGx5KEJWX3VzYWdlLCAyLCBmdW5jdGlvbih4KSBhbGwoaXMubmEoeCkpKV0KQVZfdXNhZ2UgPSBnZW5lVXNhZ2UoY2xuX2dsaW9fbGlzdCwgSFVNQU5fVFJBViwgLm5vcm0gPSBUKQpyb3duYW1lcyhBVl91c2FnZSkgPSBBVl91c2FnZVssMV0KQVZfdXNhZ2UgPSBBVl91c2FnZVssLTFdCkFWX3VzYWdlID0gQVZfdXNhZ2VbLCFhcHBseShBVl91c2FnZSwgMiwgZnVuY3Rpb24oeCkgYWxsKGlzLm5hKHgpKSldCgojIEogc2VnbWVudCB1c2FnZQpCSl91c2FnZSA9IGdlbmVVc2FnZShjbG5fZ2xpb19saXN0LCBIVU1BTl9UUkJKLCAubm9ybSA9IFQpCnJvd25hbWVzKEJKX3VzYWdlKSA9IEJKX3VzYWdlWywxXQpCSl91c2FnZSA9IEJKX3VzYWdlWywtMV0KQkpfdXNhZ2UgPSBCSl91c2FnZVssIWFwcGx5KEJKX3VzYWdlLCAyLCBmdW5jdGlvbih4KSBhbGwoaXMubmEoeCkpKV0KQUpfdXNhZ2UgPSBnZW5lVXNhZ2UoY2xuX2dsaW9fbGlzdCwgSFVNQU5fVFJBSiwgLm5vcm0gPSBUKQpyb3duYW1lcyhBSl91c2FnZSkgPSBBSl91c2FnZVssMV0KQUpfdXNhZ2UgPSBBSl91c2FnZVssLTFdCkFKX3VzYWdlID0gQUpfdXNhZ2VbLCFhcHBseShBSl91c2FnZSwgMiwgZnVuY3Rpb24oeCkgYWxsKGlzLm5hKHgpKSldCgpib3RoX3VzYWdlID0gbGlzdCgpCmZvcihuIGluIG5hbWVzKGNsbl9nbGlvX2xpc3QpKXsKICBpZihtZXRhZGF0YVtuLCJyZXBlcnRvaXJlIl09PSJUQ1JCIil7CiAgICBib3RoX3VzYWdlW1tuXV0gPSBnZW5lVXNhZ2UoY2xuX2dsaW9fbGlzdFtbbl1dLCBsaXN0KEhVTUFOX1RSQlYsIEhVTUFOX1RSQkopLCAubm9ybSA9IFQpCiAgfSBlbHNlewogICAgYm90aF91c2FnZVtbbl1dID0gZ2VuZVVzYWdlKGNsbl9nbGlvX2xpc3RbW25dXSwgbGlzdChIVU1BTl9UUkFWLCBIVU1BTl9UUkFKKSwgLm5vcm0gPSBUKQogIH0KfQoKIyBob3cgYXJlIHRoZSBkaWZmZXJlbnQgc2FtcGxlcyBkaXN0aW5ndWlzaGVkIGJ5IHNlZ21lbnQgdXNhZ2U/CnBoZWF0bWFwOjpwaGVhdG1hcCh0KEJWX3VzYWdlKSkKcGhlYXRtYXA6OnBoZWF0bWFwKHQoQVZfdXNhZ2UpKQpwaGVhdG1hcDo6cGhlYXRtYXAodChCSl91c2FnZSkpCnBoZWF0bWFwOjpwaGVhdG1hcCh0KEFKX3VzYWdlKSkKCiMgd2hhdCBhcmUgdGhlIHNlZ21lbnQgY29tYmluYXRpb24gYmlhc2VzIHBlciBzYW1wbGU/CnBoZWF0bWFwOjpwaGVhdG1hcCh0KGJvdGhfdXNhZ2UkZ2xpb21hX1RDUkJfYmxvb2QpLCBtYWluID0gImdsaW9tYV9UQ1JCX2Jsb29kIikKcGhlYXRtYXA6OnBoZWF0bWFwKHQoYm90aF91c2FnZSRnbGlvbWFfVENSQl9icmFpbiksIG1haW4gPSAiZ2xpb21hX1RDUkJfYnJhaW4iKQpwaGVhdG1hcDo6cGhlYXRtYXAodChib3RoX3VzYWdlJGhlYWx0aHlfVENSQl9ibG9vZCksIG1haW4gPSAiaGVhbHRoeV9UQ1JCX2Jsb29kIikKcGhlYXRtYXA6OnBoZWF0bWFwKHQoYm90aF91c2FnZSRoZWFsdGh5X1RDUkJfYnJhaW4pLCBtYWluID0gImhlYWx0aHlfVENSQl9icmFpbiIpCmBgYAoKCgojIyBHZW5lIHNlZ21lbnQgY29tcGFyaXNvbiBieSBpbmZvcm1hdGlvbiBkaXN0YW5jZQpJbmZvcm1hdGlvbiBvbiBkaXZlcnNpdHkgbWVhc3VyZXM6IGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0RpdmVyc2l0eV9pbmRleApFbnRyb3B5IC0gbWVhc3VyZSBvZiBob3cgdW5wcmVkaWN0YWJsZSB0aGUgZGF0YSBpcy4gVGhlIGxhcmdlciB0aGUgZW50cm9weSwgdGhlIG1vcmUgZGl2ZXJzZSAoaW4gdGhpcyBjYXNlIHRoZSByZXBlcnRvaXJlKS4KQ29tcHV0ZSByZXBlcnRvaXJlIGVudHJvcHkKCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQojIFYgc2VnbWVudCB1c2FnZSBlbnRyb3B5CkJWX2VudHJvcHkgPSBlbnRyb3B5LnNlZyhjbG5fZ2xpb19saXN0W3Jvd25hbWVzKG1ldGFkYXRhW21ldGFkYXRhJHJlcGVydG9pcmU9PSJUQ1JCIixdKV0sIEhVTUFOX1RSQlYpCkFWX2VudHJvcHkgPSBlbnRyb3B5LnNlZyhjbG5fZ2xpb19saXN0W3Jvd25hbWVzKG1ldGFkYXRhW21ldGFkYXRhJHJlcGVydG9pcmU9PSJUQ1JBIixdKV0sIEhVTUFOX1RSQVYpCgojIEogc2VnbWVudCB1c2FnZSBlbnRyb3B5CkJKX2VudHJvcHkgPSBlbnRyb3B5LnNlZyhjbG5fZ2xpb19saXN0W3Jvd25hbWVzKG1ldGFkYXRhW21ldGFkYXRhJHJlcGVydG9pcmU9PSJUQ1JCIixdKV0sIEhVTUFOX1RSQkopCkFKX2VudHJvcHkgPSBlbnRyb3B5LnNlZyhjbG5fZ2xpb19saXN0W3Jvd25hbWVzKG1ldGFkYXRhW21ldGFkYXRhJHJlcGVydG9pcmU9PSJUQ1JBIixdKV0sIEhVTUFOX1RSQUopCgoKIyBwbG90ClRDUkJfZGYgPSBkYXRhLmZyYW1lKFZfc2VnID0gQlZfZW50cm9weSwKICAgICAgICAgICAgICAgICAgICAgSl9zZWcgPSBCSl9lbnRyb3B5KQpUQ1JCX2RmID0gbWVyZ2UoVENSQl9kZiwgbWV0YWRhdGEsIGJ5ID0gMCkKCmdncGxvdChUQ1JCX2RmLCBhZXMoeCA9IFZfc2VnLCB5ID0gSl9zZWcsIGNvbG91ciA9IGNvbmRpdGlvbiwgc2hhcGUgPSB0aXNzdWUpKSsKICBnZW9tX3BvaW50KCkrCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMiwgMTkpKSsKICBnZ3RpdGxlKCJUQ1IgYmV0YSBnZW5lIHNlZ21lbnQgZW50cm9weSIpKwogIHRoZW1lX2NsYXNzaWMoKSsKICB0aGVtZShhc3BlY3QucmF0aW8gPSAxKQoKVENSQV9kZiA9IGRhdGEuZnJhbWUoVl9zZWcgPSBBVl9lbnRyb3B5LAogICAgICAgICAgICAgICAgICAgICBKX3NlZyA9IEFKX2VudHJvcHkpClRDUkFfZGYgPSBtZXJnZShUQ1JBX2RmLCBtZXRhZGF0YSwgYnkgPSAwKQpnZ3Bsb3QoVENSQV9kZiwgYWVzKHggPSBWX3NlZywgeSA9IEpfc2VnLCBjb2xvdXIgPSBjb25kaXRpb24sIHNoYXBlID0gdGlzc3VlKSkrCiAgZ2VvbV9wb2ludCgpKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKDIsIDE5KSkrCiAgZ2d0aXRsZSgiVENSIGFscGhhIGdlbmUgc2VnbWVudCBlbnRyb3B5IikrCiAgdGhlbWVfY2xhc3NpYygpKwogIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpCmBgYAoKCgpPdGhlciBtZWFzdXJlcyBvZiByZXBlcnRvaXJlIGRpdmVyc2l0eQoKYGBge3J9CiMgVHJ1ZSBkaXZlcnNpdHkgLSB0aGUgbW9zdCBmdW5kYW1lbnRhbCBtZWFzdXJlIG9mIGRpdmVyc2l0eSwgYSBmZXcgb3RoZXJzIGFyZSBkZXJpdmVkIGZyb20gdGhpcyBvbmUKcmVwRGl2ZXJzaXR5KGNsbl9nbGlvX2xpc3Rbcm93bmFtZXMobWV0YWRhdGFbbWV0YWRhdGEkcmVwZXJ0b2lyZT09IlRDUkIiLF0pXSwgJ2RpdicsICdyZWFkLmNvdW50JywgCiAgICAgICAgICAgICAubm9ybSA9IFQsIC5kby5ub3JtID0gVCkKcmVwRGl2ZXJzaXR5KGNsbl9nbGlvX2xpc3Rbcm93bmFtZXMobWV0YWRhdGFbbWV0YWRhdGEkcmVwZXJ0b2lyZT09IlRDUkEiLF0pXSwgJ2RpdicsICdyZWFkLmNvdW50JywgCiAgICAgICAgICAgICAubm9ybSA9IFQsIC5kby5ub3JtID0gVCkKCiMgR2luaSBjb2VmZmljaWVudCAtIG1lYXN1cmUgb2YgaW5lcXVhbGl0eSBpbiB0aGUgZGF0YSAodGhlIGxhcmdlciB0aGUgbW9yZSB1bmVxdWFsKSAodmVyeSB1c2VkIGluIGVjb25vbXkpCnJlcERpdmVyc2l0eShjbG5fZ2xpb19saXN0W3Jvd25hbWVzKG1ldGFkYXRhW21ldGFkYXRhJHJlcGVydG9pcmU9PSJUQ1JCIixdKV0sICdnaW5pJywgJ3JlYWQucHJvcCcsIAogICAgICAgICAgICAgLm5vcm0gPSBULCAuZG8ubm9ybSA9IFQpCnJlcERpdmVyc2l0eShjbG5fZ2xpb19saXN0W3Jvd25hbWVzKG1ldGFkYXRhW21ldGFkYXRhJHJlcGVydG9pcmU9PSJUQ1JBIixdKV0sICdnaW5pJywgJ3JlYWQucHJvcCcsIAogICAgICAgICAgICAgLm5vcm0gPSBULCAuZG8ubm9ybSA9IFQpCmBgYAoKCgpDb21wdXRlIGRpdmVyZ2VuY2UgYmV0d2VlbiBzYW1wbGVzCkplbnNlbi1TaGFubm9uIGRpdmVyZ2VuY2UgLSBzaW1pbGFyaXR5IGJldHdlZW4gdHdvIHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbnMuIFJlbGF0ZWQgdG8gdGhlIEt1bGxiYWNr4oCTTGVpYmxlciBkaXZlcmdlbmNlLCBhIG1lYXN1cmUgb2YgInJlbGF0aXZlIGVudHJvcHkiLgoKYGBge3J9CmpzX2xpc3QgPSBsaXN0KAogIEJWX2pzID0ganMuZGl2LnNlZyhjbG5fZ2xpb19saXN0W3Jvd25hbWVzKG1ldGFkYXRhW21ldGFkYXRhJHJlcGVydG9pcmU9PSJUQ1JCIixdKV0sIAogICAgICAgICAgICAgICAgICAgICBIVU1BTl9UUkJWLCAudmVyYm9zZSA9IEYpLAogIEFWX2pzID0ganMuZGl2LnNlZyhjbG5fZ2xpb19saXN0W3Jvd25hbWVzKG1ldGFkYXRhW21ldGFkYXRhJHJlcGVydG9pcmU9PSJUQ1JBIixdKV0sIAogICAgICAgICAgICAgICAgICAgICBIVU1BTl9UUkFWLCAudmVyYm9zZSA9IEYpLAogIAogIEJKX2pzID0ganMuZGl2LnNlZyhjbG5fZ2xpb19saXN0W3Jvd25hbWVzKG1ldGFkYXRhW21ldGFkYXRhJHJlcGVydG9pcmU9PSJUQ1JCIixdKV0sIAogICAgICAgICAgICAgICAgICAgICBIVU1BTl9UUkJKLCAudmVyYm9zZSA9IEYpLAogIEFKX2pzID0ganMuZGl2LnNlZyhjbG5fZ2xpb19saXN0W3Jvd25hbWVzKG1ldGFkYXRhW21ldGFkYXRhJHJlcGVydG9pcmU9PSJUQ1JBIixdKV0sIAogICAgICAgICAgICAgICAgICAgICBIVU1BTl9UUkFKLCAudmVyYm9zZSA9IEYpCikKCmZvcihuIGluIG5hbWVzKGpzX2xpc3QpKXtwaGVhdG1hcDo6cGhlYXRtYXAoanNfbGlzdFtbbl1dLCBtYWluID0gbil9CmBgYAoKCgpEaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24gYnkgUENBIHdpdGggZ2VuZSB1c2FnZSAtIGhvdyBkbyBkaWZmZXJlbnQgc2FtcGxlcyBjbHVzdGVyIGJhc2VkIG9uIHRoZWlyIGV4cHJlc3Npb24gb2YgVkogc2VnbWVudHM/ClRoaXMgY2FzZSB3aWxsIG5vdCBiZSB2ZXJ5IGluZm9ybWF0aXZlIGFzIHdlIGFyZSBvbmx5IGxvb2tpbmcgYXQgNCBzYW1wbGVzLgoKYGBge3J9CiMgdXNlIEFKIGFuZCBBViBjb21iaW5hdGlvbgpwY2FfMmQgPSBwY2Euc2VnbWVudHMuMkQoY2xuX2dsaW9fbGlzdFtyb3duYW1lcyhtZXRhZGF0YVttZXRhZGF0YSRyZXBlcnRvaXJlPT0iVENSQSIsXSldLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuZ2VuZXMgPSAgbGlzdChIVU1BTl9UUkFWLCBIVU1BTl9UUkFKKSwgLmRvLnBsb3QgPSBGKQoKcGxvdF9kZiA9IG1lcmdlKHBjYV8yZCR4LCBtZXRhZGF0YSwgYnkgPSAwKQoKZ2dwbG90KHBsb3RfZGYsIGFlcyh4ID0gUEMxLCB5ID0gUEMyLCBjb2xvdXIgPSBjb25kaXRpb24sIHNoYXBlID0gdGlzc3VlKSkrCiAgZ2VvbV9wb2ludChzaXplID0gMikrCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMTksIDIpKSsKICBnZ3RpdGxlKCJUQ1IgYWxwaGEgUENBIikrCiAgdGhlbWVfY2xhc3NpYygpKwogIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKCiMgdXNlIEJKIGFuZCBCViBjb21iaW5hdGlvbgpwY2FfMmQgPSBwY2Euc2VnbWVudHMuMkQoY2xuX2dsaW9fbGlzdFtyb3duYW1lcyhtZXRhZGF0YVttZXRhZGF0YSRyZXBlcnRvaXJlPT0iVENSQiIsXSldLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuZ2VuZXMgPSAgbGlzdChIVU1BTl9UUkJWLCBIVU1BTl9UUkJKKSwgLmRvLnBsb3QgPSBGKQoKcGxvdF9kZiA9IG1lcmdlKHBjYV8yZCR4LCBtZXRhZGF0YSwgYnkgPSAwKQoKZ2dwbG90KHBsb3RfZGYsIGFlcyh4ID0gUEMxLCB5ID0gUEMyLCBjb2xvdXIgPSBjb25kaXRpb24sIHNoYXBlID0gdGlzc3VlKSkrCiAgZ2VvbV9wb2ludChzaXplID0gMikrCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMTksIDIpKSsKICBnZ3RpdGxlKCJUQ1IgYmV0YSBQQ0EiKSsKICB0aGVtZV9jbGFzc2ljKCkrCiAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCgoKIyBDb21wYXJpbmcgcmVwZXJ0b2lyZXMKQ29tcHV0ZSBhIHBhaXJ3aXNlIG5vcm1hbGlzZWQgbnVtYmVyIG9mIHNoYXJlZCBjbG9ub3R5cGVzIGFuZCBwbG90IGEgaGVhdG1hcCBvZiB0aGVtCk5vcm1hbGlzYXRpb24gaXMgaW1wb3J0YW50IHRvIGF2b2lkIGJpc2FzZXMgYnkgZGlmZmVyZW50IGxpYnJhcnkgc2l6ZSBhbmQgY2xvbm90eXBlIGNhcHR1cmUKCmBgYHtyfQojIFRDUkEKb3ZlbGFwX0FfbnVjX2V4YWN0IDwtIHJlcE92ZXJsYXAoY2xuX2dsaW9fbGlzdFtyb3duYW1lcyhtZXRhZGF0YVttZXRhZGF0YSRyZXBlcnRvaXJlPT0iVENSQSIsXSldLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImV4YWN0IiwgLm5vcm0gPSBULCAuc2VxID0gIm51YyIpCm92ZWxhcF9BX2FhX2V4YWN0IDwtIHJlcE92ZXJsYXAoY2xuX2dsaW9fbGlzdFtyb3duYW1lcyhtZXRhZGF0YVttZXRhZGF0YSRyZXBlcnRvaXJlPT0iVENSQSIsXSldLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZXhhY3QiLCAubm9ybSA9IFQsIC5zZXEgPSAiYWEiKQp2aXMuaGVhdG1hcChvdmVsYXBfQV9udWNfZXhhY3QsIC50ZXh0ID0gRiwgLnRpdGxlID0gIlRDUkEgY2xvbm90eXBlIG92ZXJsYXBcbk51Y2xlb3RpZGUgU2VxdWVuY2UiKQp2aXMuaGVhdG1hcChvdmVsYXBfQV9hYV9leGFjdCwgLnRleHQgPSBGLCAudGl0bGUgPSAiVENSQSBjbG9ub3R5cGUgb3ZlcmxhcFxuQW1pbm9hY2lkIFNlcXVlbmNlIikKCiMgVENSQgpvdmVsYXBfQl9udWNfZXhhY3QgPC0gcmVwT3ZlcmxhcChjbG5fZ2xpb19saXN0W3Jvd25hbWVzKG1ldGFkYXRhW21ldGFkYXRhJHJlcGVydG9pcmU9PSJUQ1JCIixdKV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZXhhY3QiLCAubm9ybSA9IFQsIC5zZXEgPSAibnVjIikKb3ZlbGFwX0JfYWFfZXhhY3QgPC0gcmVwT3ZlcmxhcChjbG5fZ2xpb19saXN0W3Jvd25hbWVzKG1ldGFkYXRhW21ldGFkYXRhJHJlcGVydG9pcmU9PSJUQ1JCIixdKV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJleGFjdCIsIC5ub3JtID0gVCwgLnNlcSA9ICJhYSIpCnZpcy5oZWF0bWFwKG92ZWxhcF9CX251Y19leGFjdCwgLnRleHQgPSBGLCAudGl0bGUgPSAiVENSQiBjbG9ub3R5cGUgb3ZlcmxhcFxuTnVjbGVvdGlkZSBTZXF1ZW5jZSIpCnZpcy5oZWF0bWFwKG92ZWxhcF9CX2FhX2V4YWN0LCAudGV4dCA9IEYsIC50aXRsZSA9ICJUQ1JCIGNsb25vdHlwZSBvdmVybGFwXG5BbWlub2FjaWQgU2VxdWVuY2UiKQpgYGAKCgoKVG9wIGNyb3NzIC0gbnVtYmVyIG9mIHNoYXJlZCBjbG9ub3R5cGVzIGFtb25nIHRob3NlIG1vc3QgaGlnaGx5IHJlcHJlc2VudGVkCkltcG9ydGFudCBiZWNhdXNlIHRoZXNlIHRlbmQgdG8gYmUgdGhlIG1vc3QgcmVsZXZhbnQgZm9ybXMgb2YgdGhlIFRDUgoKYGBge3J9CnRjcmFfdG9wY3Jvc3MgPC0gdG9wLmNyb3NzKC5kYXRhID0gY2xuX2dsaW9fbGlzdFtyb3duYW1lcyhtZXRhZGF0YVttZXRhZGF0YSRyZXBlcnRvaXJlPT0iVENSQSIsXSldLCAKICAgICAgICAgICAgICAgICAgICAgLm4gPSBzZXEoNTAwLCAxMDAwMCwgNTAwKSwgLnZlcmJvc2UgPSBGLCAubm9ybSA9IFQpCnRvcC5jcm9zcy5wbG90KHRjcmFfdG9wY3Jvc3MpCgp0Y3JiX3RvcGNyb3NzIDwtIHRvcC5jcm9zcyguZGF0YSA9IGNsbl9nbGlvX2xpc3Rbcm93bmFtZXMobWV0YWRhdGFbbWV0YWRhdGEkcmVwZXJ0b2lyZT09IlRDUkIiLF0pXSwgCiAgICAgICAgICAgICAgICAgICAgIC5uID0gc2VxKDUwMCwgMTAwMDAsIDUwMCksIC52ZXJib3NlID0gRiwgLm5vcm0gPSBUKQp0b3AuY3Jvc3MucGxvdCh0Y3JiX3RvcGNyb3NzKQpgYGAKCgoKU2hhcmVkIHJlcGVydG9pcmUgLSBudW1iZXIgb2Ygc2hhcmVkIGNsb25vdHlwZXMgZm9yIGVhY2ggcmVwZXJ0b2lyZSBmb3IgZWFjaCBkZWdyZWUgb2Ygc2hhcmluZyAoaS5lLiwgbnVtYmVyIG9mIHNhbXBsZXMgaW4gd2hpY2ggaW5kaWNhdGVkIGFtb3VudCBvZiBjbG9uZXMgaGF2ZSBiZWVuIGZvdW5kKS4KCmBgYHtyfQpzaGFyZWRfQV9yZXAgPSBzaGFyZWQucmVwZXJ0b2lyZShjbG5fZ2xpb19saXN0W3Jvd25hbWVzKG1ldGFkYXRhW21ldGFkYXRhJHJlcGVydG9pcmU9PSJUQ1JBIixdKV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAubWluLnBwbCA9IDIsIC5jbGVhciA9IFQpCnNoYXJlZC5yZXByZXNlbnRhdGlvbihzaGFyZWRfQV9yZXApCgpzaGFyZWRfQl9yZXAgPSBzaGFyZWQucmVwZXJ0b2lyZShjbG5fZ2xpb19saXN0W3Jvd25hbWVzKG1ldGFkYXRhW21ldGFkYXRhJHJlcGVydG9pcmU9PSJUQ1JCIixdKV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAubWluLnBwbCA9IDIsIC5jbGVhciA9IFQpCnNoYXJlZC5yZXByZXNlbnRhdGlvbihzaGFyZWRfQl9yZXApCmBgYAoKCgpTaGFyZWQgcmVwZXJ0b2lyZSBtdXRhdGlvbiBuZXR3b3JrcyAtIGhvdyBkbyB0aGUgZGlmZmVyZW50IGNsb25vdHlwZXMgcmVsYXRlIHRvIGVhY2ggb3RoZXIgYnkgc2VxdWVuY2Ugc2ltaWxhcml0eT8KCmBgYHtyfQojIFRDUkJBCnNoYXJlZF9BX3JlcF90b3AgPSBzaGFyZWQucmVwZXJ0b2lyZShjbG5fZ2xpb19saXN0W3Jvd25hbWVzKG1ldGFkYXRhW21ldGFkYXRhJHJlcGVydG9pcmU9PSJUQ1JBIixdKV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLm1pbi5wcGwgPSAyLCAuY2xlYXIgPSBULCAuaGVhZCA9IDIwMDApCkdfQSA8LSBtdXRhdGlvbi5uZXR3b3JrKHNoYXJlZF9BX3JlcF90b3AsIC5tYXguZXJyb3JzID0gMykKCiMjIHBsb3R0aW5nIGF0dHJpYnV0ZXMKVihHX0EpJGNvbG9yID0gbWV0YWRhdGFbbWV0YWRhdGEkcmVwZXJ0b2lyZT09IlRDUkEiLCJjb25kaXRpb24iXQpWKEdfQSkkc2hhcGUgPSBtZXRhZGF0YVttZXRhZGF0YSRyZXBlcnRvaXJlPT0iVENSQSIsInRpc3N1ZSJdCgojIyBuZXR3b3JrIGxheW91dApsX0EgPSBpZ3JhcGg6OmxheW91dC5mcnVjaHRlcm1hbi5yZWluZ29sZChHX0EpCgojIFRDUkIKc2hhcmVkX0JfcmVwX3RvcCA9IHNoYXJlZC5yZXBlcnRvaXJlKGNsbl9nbGlvX2xpc3Rbcm93bmFtZXMobWV0YWRhdGFbbWV0YWRhdGEkcmVwZXJ0b2lyZT09IlRDUkIiLF0pXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAubWluLnBwbCA9IDIsIC5jbGVhciA9IFQsIC5oZWFkID0gMjAwMCkKR19CIDwtIG11dGF0aW9uLm5ldHdvcmsoc2hhcmVkX0JfcmVwX3RvcCwgLm1heC5lcnJvcnMgPSAzKQoKIyMgcGxvdHRpbmcgYXR0cmlidXRlcwpWKEdfQikkY29sb3IgPSBtZXRhZGF0YVttZXRhZGF0YSRyZXBlcnRvaXJlPT0iVENSQiIsImNvbmRpdGlvbiJdClYoR19CKSRzaGFwZSA9IG1ldGFkYXRhW21ldGFkYXRhJHJlcGVydG9pcmU9PSJUQ1JCIiwidGlzc3VlIl0KCiMjIG5ldHdvcmsgbGF5b3V0CmxfQiA9IGlncmFwaDo6bGF5b3V0LmZydWNodGVybWFuLnJlaW5nb2xkKEdfQikKYGBgCgoKCk1ha2luZyB0aGUgYWN0dWFsIG5ldHdvcmsgcGxvdApNb3JlIGluZm86IGh0dHBzOi8vcGxvdC5seS9yL25ldHdvcmstZ3JhcGhzLwoKYGBge3J9CiMgcGxvdGluZyBmdW5jdGlvbgptYWtlUGxvdGx5TmV0d29yayA9IGZ1bmN0aW9uKGlncmFwaF9vYmosIGxheW91dF9uZXQsIHRpdGxlX25ldCl7CiAgdnMgPC0gVihpZ3JhcGhfb2JqKQogIGVzIDwtIGFzLmRhdGEuZnJhbWUoZ2V0LmVkZ2VsaXN0KGlncmFwaF9vYmopKQogIAogIE52IDwtIGxlbmd0aCh2cykKICBOZSA8LSBsZW5ndGgoZXNbMV0kVjEpCiAgCiAgWG4gPC0gbGF5b3V0X25ldFssMV0KICBZbiA8LSBsYXlvdXRfbmV0WywyXQogIAogIG5ldHdvcmsgPC0gcGxvdGx5OjpwbG90X2x5KHggPSB+WG4sIHkgPSB+WW4sIG1vZGUgPSAibWFya2VycyIsIHRleHQgPSB2cyRsYWJlbCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaG92ZXJpbmZvID0gInRleHQiLCBjb2xvciA9IHZzJGNvbG9yLCBzeW1ib2wgPSB2cyRzaGFwZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3ltYm9scyA9IGMoIjE5IiwgIjIiKSwgY29sb3JzID0gYygiI0ZGMDAwNiIsICIjMDA4MEZGIikpCiAgZWRnZV9zaGFwZXMgPC0gbGlzdCgpCiAgZm9yKGkgaW4gMTpOZSkgewogICAgdjAgPC0gZXNbaSxdJFYxCiAgICB2MSA8LSBlc1tpLF0kVjIKICAKICAgIGVkZ2Vfc2hhcGUgPSBsaXN0KAogICAgICB0eXBlID0gImxpbmUiLAogICAgICBsaW5lID0gbGlzdChjb2xvciA9ICIjMDMwMzAzIiwgd2lkdGggPSAwLjMpLAogICAgICB4MCA9IFhuW3YwXSwKICAgICAgeTAgPSBZblt2MF0sCiAgICAgIHgxID0gWG5bdjFdLAogICAgICB5MSA9IFluW3YxXQogICAgKQogIAogICAgZWRnZV9zaGFwZXNbW2ldXSA8LSBlZGdlX3NoYXBlCiAgfQogIAogIGF4aXMgPC0gbGlzdCh0aXRsZSA9ICIiLCBzaG93Z3JpZCA9IEZBTFNFLCBzaG93dGlja2xhYmVscyA9IEZBTFNFLCB6ZXJvbGluZSA9IEZBTFNFKQogIAogIHAgPC0gcGxvdGx5OjpsYXlvdXQoCiAgICBuZXR3b3JrLAogICAgdGl0bGUgPSB0aXRsZV9uZXQsCiAgICBzaGFwZXMgPSBlZGdlX3NoYXBlcywKICAgIHhheGlzID0gYXhpcywKICAgIHlheGlzID0gYXhpcwogICkKfQoKIyBtYWtlIHRoZSBwbG90cwpwID0gbWFrZVBsb3RseU5ldHdvcmsoR19BLCBsX0EsICJUQ1JBIG11dGF0aW9uIGdyYXBoIikKcApwID0gbWFrZVBsb3RseU5ldHdvcmsoR19CLCBsX0IsICJUQ1JCIG11dGF0aW9uIGdyYXBoIikKcApgYGAKCgoKCgoKCg==